package com.yilian.reflection;

import java.lang.reflect.*;
import java.lang.annotation.Annotation;
import java.util.*;

/**
 * Java反射练习类
 * 
 * 这个类包含了各种实际应用场景的反射练习，展示了反射在实际开发中的用途：
 * 1. 属性复制工具 - 实现对象间的属性值复制
 * 2. 方法调用器 - 动态调用对象的方法
 * 3. 对象比较器 - 比较两个对象的属性值
 * 4. 配置加载器 - 将配置信息动态加载到对象中
 * 5. 注解处理器 - 处理类、字段、方法的注解信息
 * 6. 序列化工具 - 对象与Map之间的转换
 * 
 * 学习要点：
 * - 这些练习展示了反射在实际项目中的应用
 * - 每个练习都包含了完整的实现逻辑和异常处理
 * - 这些工具类可以扩展为通用的框架组件
 * - 理解这些应用有助于掌握反射的高级用法
 * 
 * @author 练习项目
 * @version 1.0
 */
public class ReflectionPractice {
    
    /**
     * 主方法 - 程序入口点
     * 依次执行各种反射应用练习
     * 
     * @param args 命令行参数（未使用）
     */
    public static void main(String[] args) {
        System.out.println("=== Java反射练习 ===\n");
        
        // 练习1：属性复制工具
        practicePropertyCopy();
        
        // 练习2：方法调用器
        practiceMethodInvoker();
        
        // 练习3：对象比较器
        practiceObjectComparator();
        
        // 练习4：配置加载器
        practiceConfigLoader();
        
        // 练习5：注解处理器
        practiceAnnotationProcessor();
        
        // 练习6：序列化工具
        practiceSerializationTool();
    }
    
    /**
     * 练习1：属性复制工具
     * 
     * 通过反射将一个对象的属性值复制到另一个对象，这是实际开发中常见的需求。
     * 应用场景：
     * - DTO与实体对象之间的转换
     * - 对象克隆和复制
     * - 数据映射和转换
     * 
     * 实现原理：
     * - 获取源对象和目标对象的Class信息
     * - 遍历源对象的所有字段
     * - 在目标对象中查找同名字段
     * - 通过反射复制字段值
     * 
     * 注意事项：
     * - 只复制同名字段
     * - 需要处理字段访问权限
     * - 类型兼容性检查
     */
    public static void practicePropertyCopy() {
        System.out.println("练习1: 属性复制工具");
        System.out.println("----------------------------------------");
        
        try {
            // 创建源对象
            Person source = new Person("源对象", 25);
            source.setAge(30);
            
            // 创建目标对象
            Person target = new Person();
            
            System.out.println("复制前:");
            System.out.println("源对象: " + source);
            System.out.println("目标对象: " + target);
            
            // 执行属性复制
            copyProperties(source, target);
            
            System.out.println("复制后:");
            System.out.println("源对象: " + source);
            System.out.println("目标对象: " + target);
            
        } catch (Exception e) {
            System.err.println("属性复制时发生异常: " + e.getMessage());
            e.printStackTrace();
        }
        System.out.println();
    }
    
    /**
     * 属性复制方法
     * 
     * 核心实现逻辑：
     * 1. 获取源对象和目标对象的Class信息
     * 2. 遍历源对象的所有字段（包括私有字段）
     * 3. 在目标对象中查找同名字段
     * 4. 设置字段可访问并复制值
     * 
     * @param source 源对象
     * @param target 目标对象
     * @throws Exception 反射操作可能抛出的异常
     */
    public static void copyProperties(Object source, Object target) throws Exception {
        Class<?> sourceClass = source.getClass();
        Class<?> targetClass = target.getClass();
        
        // 获取源对象的所有字段（包括私有字段）
        Field[] sourceFields = sourceClass.getDeclaredFields();
        
        for (Field sourceField : sourceFields) {
            try {
                // 获取目标对象中同名的字段
                Field targetField = targetClass.getDeclaredField(sourceField.getName());
                
                // 设置字段可访问（绕过访问控制）
                sourceField.setAccessible(true);
                targetField.setAccessible(true);
                
                // 复制值：从源对象读取，写入目标对象
                Object value = sourceField.get(source);
                targetField.set(target, value);
                
            } catch (NoSuchFieldException e) {
                // 目标对象中没有对应字段，跳过
                // 这是正常情况，不是所有字段都需要复制
                continue;
            }
        }
    }
    
    /**
     * 练习2：方法调用器
     * 
     * 通过反射动态调用对象的方法，实现方法调用的解耦。
     * 应用场景：
     * - 插件系统中的方法调用
     * - 配置驱动的业务逻辑
     * - 动态方法路由
     * - 测试框架中的方法调用
     * 
     * 实现原理：
     * - 根据方法名和参数类型获取Method对象
     * - 通过Method.invoke()调用方法
     * - 处理参数类型匹配和转换
     * 
     * 注意事项：
     * - 方法名必须存在
     * - 参数类型必须匹配
     * - 异常处理和类型安全
     */
    public static void practiceMethodInvoker() {
        System.out.println("练习2: 方法调用器");
        System.out.println("----------------------------------------");
        
        try {
            Person person = new Person("方法调用测试", 22);
            
            // 调用无参数方法
            invokeMethod(person, "introduce");
            
            // 调用带参数方法
            invokeMethod(person, "introduce", "Hello");
            
            // 调用getter方法
            Object name = invokeMethod(person, "getName");
            System.out.println("获取到的姓名: " + name);
            
            // 调用setter方法
            invokeMethod(person, "setAge", 35);
            System.out.println("设置年龄后: " + person);
            
        } catch (Exception e) {
            System.err.println("方法调用时发生异常: " + e.getMessage());
            e.printStackTrace();
        }
        System.out.println();
    }
    
    /**
     * 方法调用方法
     * 
     * 核心实现逻辑：
     * 1. 获取对象的Class信息
     * 2. 确定参数类型数组
     * 3. 根据方法名和参数类型获取Method对象
     * 4. 调用Method.invoke()执行方法
     * 
     * @param obj 要调用方法的对象
     * @param methodName 方法名
     * @param args 方法参数
     * @return 方法返回值
     * @throws Exception 反射操作可能抛出的异常
     */
    public static Object invokeMethod(Object obj, String methodName, Object... args) throws Exception {
        Class<?> objClass = obj.getClass();
        
        // 确定参数类型数组
        // 这是关键步骤，反射需要精确的参数类型信息
        Class<?>[] paramTypes = new Class[args.length];
        for (int i = 0; i < args.length; i++) {
            // 处理null参数的情况
            paramTypes[i] = args[i] != null ? args[i].getClass() : Object.class;
        }
        
        // 获取方法对象
        Method method = objClass.getMethod(methodName, paramTypes);
        
        // 调用方法并返回结果
        return method.invoke(obj, args);
    }
    
    /**
     * 练习3：对象比较器
     * 
     * 通过反射比较两个对象的属性值，实现通用的对象比较功能。
     * 应用场景：
     * - 单元测试中的对象比较
     * - 数据验证和一致性检查
     * - 对象差异分析
     * - 缓存失效判断
     * 
     * 实现原理：
     * - 获取对象的所有字段
     * - 逐个比较字段值
     * - 生成详细的差异报告
     * 
     * 注意事项：
     * - 只比较同类型对象
     * - 处理null值的情况
     * - 深度比较vs浅度比较的选择
     */
    public static void practiceObjectComparator() {
        System.out.println("练习3: 对象比较器");
        System.out.println("----------------------------------------");
        
        try {
            Person person1 = new Person("张三", 25);
            Person person2 = new Person("张三", 25);
            Person person3 = new Person("李四", 30);
            
            System.out.println("比较 person1 和 person2:");
            compareObjects(person1, person2);
            
            System.out.println("比较 person1 和 person3:");
            compareObjects(person1, person3);
            
        } catch (Exception e) {
            System.err.println("对象比较时发生异常: " + e.getMessage());
            e.printStackTrace();
        }
        System.out.println();
    }
    
    /**
     * 对象比较方法
     * 
     * 核心实现逻辑：
     * 1. 验证对象有效性（非null、类型相同）
     * 2. 获取对象的所有字段
     * 3. 逐个比较字段值
     * 4. 生成详细的比较报告
     * 
     * @param obj1 第一个对象
     * @param obj2 第二个对象
     * @throws Exception 反射操作可能抛出的异常
     */
    public static void compareObjects(Object obj1, Object obj2) throws Exception {
        // 参数验证
        if (obj1 == null || obj2 == null) {
            System.out.println("对象为空，无法比较");
            return;
        }
        
        // 类型检查
        if (!obj1.getClass().equals(obj2.getClass())) {
            System.out.println("对象类型不同，无法比较");
            return;
        }
        
        Class<?> objClass = obj1.getClass();
        Field[] fields = objClass.getDeclaredFields();
        
        boolean isEqual = true;
        
        // 逐个比较字段值
        for (Field field : fields) {
            field.setAccessible(true);
            Object value1 = field.get(obj1);
            Object value2 = field.get(obj2);
            
            // 使用Objects.equals()进行安全的比较
            if (!Objects.equals(value1, value2)) {
                System.out.println("字段 " + field.getName() + " 不相等:");
                System.out.println("  " + obj1.getClass().getSimpleName() + "1." + field.getName() + " = " + value1);
                System.out.println("  " + obj2.getClass().getSimpleName() + "2." + field.getName() + " = " + value2);
                isEqual = false;
            }
        }
        
        if (isEqual) {
            System.out.println("两个对象的所有字段都相等");
        }
    }
    
    /**
     * 练习4：配置加载器
     * 
     * 通过反射将配置信息动态加载到对象中，实现配置驱动的对象初始化。
     * 应用场景：
     * - 配置文件到对象的映射
     * - 数据库记录到对象的转换
     * - 网络请求参数到对象的绑定
     * - 测试数据的动态设置
     * 
     * 实现原理：
     * - 遍历配置Map的键值对
     * - 在对象中查找对应字段
     * - 进行类型转换和值设置
     * 
     * 注意事项：
     * - 字段名必须匹配
     * - 类型转换的安全性
     * - 配置验证和默认值处理
     */
    public static void practiceConfigLoader() {
        System.out.println("练习4: 配置加载器");
        System.out.println("----------------------------------------");
        
        try {
            // 模拟配置信息（实际项目中可能来自配置文件、数据库等）
            Map<String, Object> config = new HashMap<>();
            config.put("name", "配置加载测试");
            config.put("age", 28);
            config.put("publicField", "从配置加载的字段值");
            
            // 创建对象
            Person person = new Person();
            
            System.out.println("加载配置前: " + person);
            
            // 加载配置
            loadConfig(person, config);
            
            System.out.println("加载配置后: " + person);
            
        } catch (Exception e) {
            System.err.println("配置加载时发生异常: " + e.getMessage());
            e.printStackTrace();
        }
        System.out.println();
    }
    
    /**
     * 配置加载方法
     * 
     * 核心实现逻辑：
     * 1. 遍历配置Map的所有键值对
     * 2. 在目标对象中查找对应字段
     * 3. 进行类型转换
     * 4. 设置字段值
     * 
     * @param obj 要加载配置的对象
     * @param config 配置信息Map
     * @throws Exception 反射操作可能抛出的异常
     */
    public static void loadConfig(Object obj, Map<String, Object> config) throws Exception {
        Class<?> objClass = obj.getClass();
        
        for (Map.Entry<String, Object> entry : config.entrySet()) {
            String fieldName = entry.getKey();
            Object value = entry.getValue();
            
            try {
                // 获取字段并设置可访问
                Field field = objClass.getDeclaredField(fieldName);
                field.setAccessible(true);
                
                // 类型转换和值设置
                Object convertedValue = convertValue(value, field.getType());
                field.set(obj, convertedValue);
                
            } catch (NoSuchFieldException e) {
                // 字段不存在时的处理
                System.out.println("字段 " + fieldName + " 不存在，跳过");
            }
        }
    }
    
    /**
     * 值类型转换方法
     * 
     * 实现基本的类型转换功能，支持常见的类型转换场景。
     * 转换规则：
     * - 相同类型直接返回
     * - 数字类型之间的转换
     * - 字符串到数字的转换
     * - 任意类型到字符串的转换
     * 
     * @param value 要转换的值
     * @param targetType 目标类型
     * @return 转换后的值
     */
    public static Object convertValue(Object value, Class<?> targetType) {
        if (value == null) return null;
        
        // 如果类型兼容，直接返回
        if (targetType.isAssignableFrom(value.getClass())) {
            return value;
        }
        
        // 类型转换逻辑
        if (targetType == String.class) {
            return value.toString();
        } else if (targetType == Integer.class || targetType == int.class) {
            if (value instanceof Number) {
                return ((Number) value).intValue();
            } else if (value instanceof String) {
                return Integer.parseInt((String) value);
            }
        } else if (targetType == Long.class || targetType == long.class) {
            if (value instanceof Number) {
                return ((Number) value).longValue();
            } else if (value instanceof String) {
                return Long.parseLong((String) value);
            }
        }
        
        // 无法转换时返回原值
        return value;
    }
    
    /**
     * 练习5：注解处理器
     * 
     * 通过反射处理类、字段、方法的注解信息，这是现代Java开发中的重要功能。
     * 应用场景：
     * - 框架开发中的注解处理
     * - 代码生成和编译时检查
     * - 配置驱动的功能实现
     * - 测试框架中的注解处理
     * 
     * 实现原理：
     * - 获取类、字段、方法的注解数组
     * - 分析注解的类型和属性
     * - 根据注解信息执行相应逻辑
     * 
     * 注意事项：
     * - 注解信息在运行时可用
     * - 需要处理注解不存在的情况
     * - 注解处理是框架开发的核心
     */
    public static void practiceAnnotationProcessor() {
        System.out.println("练习5: 注解处理器");
        System.out.println("----------------------------------------");
        
        try {
            // 获取类的注解信息
            Class<?> personClass = Person.class;
            
            System.out.println("类 " + personClass.getSimpleName() + " 的注解:");
            Annotation[] classAnnotations = personClass.getAnnotations();
            for (Annotation annotation : classAnnotations) {
                System.out.println("  - " + annotation.annotationType().getSimpleName());
            }
            
            // 获取字段的注解信息
            Field[] fields = personClass.getDeclaredFields();
            for (Field field : fields) {
                Annotation[] fieldAnnotations = field.getAnnotations();
                if (fieldAnnotations.length > 0) {
                    System.out.println("字段 " + field.getName() + " 的注解:");
                    for (Annotation annotation : fieldAnnotations) {
                        System.out.println("  - " + annotation.annotationType().getSimpleName());
                    }
                }
            }
            
            // 获取方法的注解信息
            Method[] methods = personClass.getDeclaredMethods();
            for (Method method : methods) {
                Annotation[] methodAnnotations = method.getAnnotations();
                if (methodAnnotations.length > 0) {
                    System.out.println("方法 " + method.getName() + " 的注解:");
                    for (Annotation annotation : methodAnnotations) {
                        System.out.println("  - " + annotation.annotationType().getSimpleName());
                    }
                }
            }
            
        } catch (Exception e) {
            System.err.println("注解处理时发生异常: " + e.getMessage());
            e.printStackTrace();
        }
        System.out.println();
    }
    
    /**
     * 练习6：序列化工具
     * 
     * 通过反射将对象序列化为Map表示，实现对象与数据结构之间的转换。
     * 应用场景：
     * - 对象序列化和反序列化
     * - 对象到JSON的转换
     * - 数据库记录到对象的映射
     * - 缓存数据的存储和恢复
     * 
     * 实现原理：
     * - 序列化：遍历对象字段，构建Map
     * - 反序列化：遍历Map，设置对象字段
     * - 类型转换和异常处理
     * 
     * 注意事项：
     * - 只处理基本字段，不处理复杂对象引用
     * - 需要处理循环引用问题
     * - 类型转换的安全性
     */
    public static void practiceSerializationTool() {
        System.out.println("练习6: 序列化工具");
        System.out.println("----------------------------------------");
        
        try {
            Person person = new Person("序列化测试", 33);
            person.setAge(40);
            
            System.out.println("原始对象: " + person);
            
            // 序列化为Map
            Map<String, Object> serialized = serializeToMap(person);
            System.out.println("序列化后的Map:");
            for (Map.Entry<String, Object> entry : serialized.entrySet()) {
                System.out.println("  " + entry.getKey() + " = " + entry.getValue());
            }
            
            // 从Map反序列化
            Person deserialized = deserializeFromMap(serialized, Person.class);
            System.out.println("反序列化后的对象: " + deserialized);
            
        } catch (Exception e) {
            System.err.println("序列化操作时发生异常: " + e.getMessage());
            e.printStackTrace();
        }
        System.out.println();
    }
    
    /**
     * 序列化为Map
     * 
     * 核心实现逻辑：
     * 1. 创建结果Map
     * 2. 获取对象的所有字段
     * 3. 读取字段值并放入Map
     * 
     * @param obj 要序列化的对象
     * @return 包含对象字段信息的Map
     * @throws Exception 反射操作可能抛出的异常
     */
    public static Map<String, Object> serializeToMap(Object obj) throws Exception {
        Map<String, Object> map = new HashMap<>();
        
        if (obj == null) return map;
        
        Class<?> objClass = obj.getClass();
        Field[] fields = objClass.getDeclaredFields();
        
        // 遍历所有字段，读取值并放入Map
        for (Field field : fields) {
            field.setAccessible(true);
            Object value = field.get(obj);
            map.put(field.getName(), value);
        }
        
        return map;
    }
    
    /**
     * 从Map反序列化
     * 
     * 核心实现逻辑：
     * 1. 创建目标类型的对象实例
     * 2. 遍历Map的所有键值对
     * 3. 在对象中查找对应字段
     * 4. 进行类型转换和值设置
     * 
     * @param map 包含对象字段信息的Map
     * @param targetClass 目标类型
     * @param <T> 泛型类型参数
     * @return 反序列化后的对象
     * @throws Exception 反射操作可能抛出的异常
     */
    public static <T> T deserializeFromMap(Map<String, Object> map, Class<T> targetClass) throws Exception {
        // 创建目标类型的实例
        T obj = targetClass.newInstance();
        
        // 遍历Map，设置对象字段
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            String fieldName = entry.getKey();
            Object value = entry.getValue();
            
            try {
                // 获取字段并设置可访问
                Field field = targetClass.getDeclaredField(fieldName);
                field.setAccessible(true);
                
                // 类型转换和值设置
                Object convertedValue = convertValue(value, field.getType());
                field.set(obj, convertedValue);
                
            } catch (NoSuchFieldException e) {
                // 字段不存在时跳过
                continue;
            }
        }
        
        return obj;
    }
} 